#pragma once

#include <mcp/config.h>
#include <mcp/auth/authenticator.h>

#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
#include <optional>
#include <functional>

namespace mcp {
namespace auth {

/// @brief OAuth 2.1 configuration
struct OAuth2Config {
    std::string client_id;
    std::string client_secret;
    std::string auth_url;
    std::string token_url;
    std::string refresh_url;
    std::vector<std::string> scopes;
    std::string redirect_uri;
    std::string code_challenge_method = "S256";
    bool use_pkce = true;
    int timeout_seconds = 30;
    std::unordered_map<std::string, std::string> additional_params;
};

/// @brief OAuth 2.1 token response
struct OAuth2TokenResponse {
    std::string access_token;
    std::string token_type = "Bearer";
    int expires_in = 3600;
    std::optional<std::string> refresh_token;
    std::optional<std::string> scope;
    std::unordered_map<std::string, std::string> additional_data;
};

/// @brief OAuth 2.1 error response
struct OAuth2Error {
    std::string error;
    std::optional<std::string> error_description;
    std::optional<std::string> error_uri;
    std::unordered_map<std::string, std::string> additional_data;
};

/// @brief OAuth 2.1 authenticator
class OAuth2Authenticator : public Authenticator {
public:
    explicit OAuth2Authenticator(const OAuth2Config& config);
    ~OAuth2Authenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

    /// @brief Get authorization URL
    /// @param state OAuth state parameter
    /// @param code_verifier PKCE code verifier
    /// @return Authorization URL
    std::string get_authorization_url(const std::string& state,
                                      const std::string& code_verifier) const;

    /// @brief Exchange authorization code for tokens
    /// @param code Authorization code
    /// @param code_verifier PKCE code verifier
    /// @param redirect_uri Redirect URI used in authorization request
    /// @return Token response or error
    std::expected<OAuth2TokenResponse, OAuth2Error> exchange_code(
        const std::string& code,
        const std::string& code_verifier,
        const std::string& redirect_uri);

    /// @brief Refresh access token
    /// @param refresh_token Refresh token
    /// @return Token response or error
    std::expected<OAuth2TokenResponse, OAuth2Error> refresh_token(
        const std::string& refresh_token);

    /// @brief Generate PKCE code verifier and challenge
    /// @return Pair of {verifier, challenge}
    static std::pair<std::string, std::string> generate_pkce_codes();

    /// @brief Get configuration
    /// @return OAuth 2.1 configuration
    const OAuth2Config& get_config() const;

    /// @brief Update configuration
    /// @param config New configuration
    void update_config(const OAuth2Config& config);

private:
    OAuth2Config config_;
    std::string generate_code_verifier() const;
    std::string generate_code_challenge(const std::string& verifier) const;
    std::string base64url_encode(const std::string& data) const;
};

/// @brief OAuth 2.1 client credentials flow authenticator
class OAuth2ClientCredentialsAuthenticator : public Authenticator {
public:
    explicit OAuth2ClientCredentialsAuthenticator(const OAuth2Config& config);
    ~OAuth2ClientCredentialsAuthenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

    /// @brief Get client credentials token
    /// @return Token response or error
    std::expected<OAuth2TokenResponse, OAuth2Error> get_client_credentials_token();

private:
    OAuth2Config config_;
};

/// @brief OAuth 2.1 device flow authenticator
class OAuth2DeviceAuthenticator : public Authenticator {
public:
    explicit OAuth2DeviceAuthenticator(const OAuth2Config& config);
    ~OAuth2DeviceAuthenticator() override = default;

    // Authenticator interface implementation
    Result<AuthResult> authenticate(const Credentials& credentials) override;
    Result<AuthResult> refresh(const std::string& refresh_token) override;
    bool validate_token(const std::string& access_token) override;
    std::string get_scheme() const override;
    std::vector<AuthChallenge> get_challenges() const override;
    bool supports_refresh() const override;

    /// @brief Device code response
    struct DeviceCodeResponse {
        std::string device_code;
        std::string user_code;
        std::string verification_uri;
        std::optional<std::string> verification_uri_complete;
        int expires_in = 300;
        int interval = 5;
    };

    /// @brief Request device code
    /// @return Device code response or error
    std::expected<DeviceCodeResponse, OAuth2Error> request_device_code();

    /// @brief Poll for token with device code
    /// @param device_code Device code
    /// @return Token response or error
    std::expected<OAuth2TokenResponse, OAuth2Error> poll_for_token(
        const std::string& device_code);

private:
    OAuth2Config config_;
};

} // namespace auth
} // namespace mcp